//
//  UILabel+Extend.swift
//  UILabel
//
//  Created by mini on 2025/4/28.
//

import Foundation
import UIKit

private var lineSpaceKey: UInt8 = 0
private var numLimitKey: UInt8 = 0

extension UILabel {
    
    // MARK: - 运行时 获取line space
    @objc var lineSpace: CGFloat {
        get {
            return objc_getAssociatedObject(self, &lineSpaceKey) as? CGFloat ?? 0
        }
        set {
            objc_setAssociatedObject(self, &lineSpaceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    @objc var fontNum: CGFloat {
        get {
            return font.pointSize
        }
        set {
            font = UIFont.systemFont(ofSize: newValue)
        }
    }
    
    @objc var numLimit: Int {
        get {
            return objc_getAssociatedObject(self, &numLimitKey) as? Int ?? 0
        }
        set {
            objc_setAssociatedObject(self, &numLimitKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    @objc var verticalText: String {
        get {
            return objc_getAssociatedObject(self, self.verticalText) as! String
        }
        set {
            objc_setAssociatedObject(self, self.verticalText, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            var str = ""
            for (index, char) in verticalText.enumerated() {
                str.append(char)
                if index != verticalText.count - 1 {
                    str.append("\n")
                }
            }
            text = str
            numberOfLines = 0
        }
    }
    
    func fitFixed(_ width: CGFloat) {
        fitWidth(width, isFixed: true)
    }
        
    func fitTitle(_ title: String, fixed width: CGFloat) {
        text = title
        fitFixed(width)
    }
        
    func fitVariable(_ width: CGFloat) {
        fitWidth(width)
    }
        
    func fitTitle(_ title: String, variable width: CGFloat) {
        text = title
        fitVariable(width)
    }
        
    private func fitWidth(_ width: CGFloat, isFixed: Bool = false) {
        guard let string = text, !string.isEmpty else {
            frame.size = CGSize(width: isFixed ? width : 0, height: font.lineHeight)
            return
        }

        let size = fetchRectWithString(string, width: width)
        var numHeightReturn = size.height

        if lineSpace != 0 && numHeightReturn > font.lineHeight {
            let attributeString = NSMutableAttributedString(string: text ?? "")
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = textAlignment
            paragraphStyle.lineSpacing = lineSpace
            attributeString.setAttributes([NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: font!], range: NSMakeRange(0, text?.count ?? 0))
            attributedText = attributeString
        }
        if numberOfLines != 0 && numHeightReturn > font.lineHeight {
            let heightLines = CGFloat(numberOfLines) * font.lineHeight + CGFloat(numberOfLines - 1) * lineSpace
            numHeightReturn = numHeightReturn > heightLines ? heightLines : numHeightReturn
        }
        frame.size = CGSize(width: isFixed ? width : size.width, height: numHeightReturn)
    }
    
    private func fetchRectWithString(_ string: String, width: CGFloat) -> CGSize {
        if(!isStr(string)){
            return CGSizeMake(0, 0)
        }

        var attributesType: [NSAttributedString.Key: Any]
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = self.lineSpace
        if(self.lineSpace>0){
            attributesType = [
                .font: self.font!,
                .paragraphStyle: paragraphStyle,
            ]
        }else{
            attributesType = [NSAttributedString.Key.font : self.font!];
        }
        let frame = string.boundingRect(with: CGSizeMake(width, CGFLOAT_MAX), options: .usesLineFragmentOrigin, attributes: attributesType, context: nil)
        if lineSpace != 0 && frame.height == font.lineHeight + lineSpace {
            return CGSize(width: frame.width, height: font.lineHeight)
        }
        return CGSize(width: frame.width, height: frame.height)
    }
}
